#include "timeoutManager.h"

#include <stdlib.h>
#include <stdint.h>

system_error_code_t timeout_manager_init(timeout_manager_t *manager, int queue_capacity, int set_size, int map_size, timeout_callback_t callback)
{
    // 参数检查
    if (manager == NULL || queue_capacity <= 0 || set_size <= 0 || map_size <= 0 || callback == NULL)
    {
        return SYSTEM_ERR_INVALID_ARGUMENT;
    }

    // 初始化互斥锁
    if (pthread_mutex_init(&manager->mutex, NULL) != 0)
    {
        return SYSTEM_ERR_WORKER_BASE; // 使用一个相关的错误码
    }

    // 初始化环形队列
    system_error_code_t ret = ring_queue_init(&manager->ring_queue, queue_capacity, set_size);
    if (ret != SYSTEM_ERR_OK)
    {
        return ret;
    }

    // 初始化全局位置映射哈希表
    ret = hash_map_init(&manager->position_map, map_size);
    if (ret != SYSTEM_ERR_OK)
    {
        // 清理已初始化的环形队列
        for (int i = 0; i < queue_capacity; ++i)
        {
            hash_set_clear(&manager->ring_queue.set[i]);
        }
        free(manager->ring_queue.set);
        return ret;
    }

    // 设置超时回调函数
    manager->timeout_callback = callback;

    return SYSTEM_ERR_OK;
}

system_error_code_t timeout_manager_update_user_activity(timeout_manager_t *manager, int fd)
{
    // 参数检查
    if (manager == NULL)
    {
        return SYSTEM_ERR_INVALID_ARGUMENT;
    }

    pthread_mutex_lock(&manager->mutex); // 加锁

    // 尝试从全局位置映射中获取文件描述符的旧环形队列索引
    intptr_t old_index_val = 0;
    system_error_code_t ret = hash_map_get(&manager->position_map, fd, &old_index_val);

    if (ret == SYSTEM_ERR_OK)
    {
        // 如果fd已存在，则从环形队列中的旧哈希集合中移除
        int old_index = (int)old_index_val;
        ret = ring_dequeue_by_index(&manager->ring_queue, fd, old_index);
        if (ret != SYSTEM_ERR_OK && ret != SYSTEM_ERR_HASHMAP_NOT_FOUND)
        {
            pthread_mutex_unlock(&manager->mutex); // 解锁并返回
            return ret;                            // 如果移除失败，则返回错误
        }
    }
    else if (ret != SYSTEM_ERR_HASHMAP_NOT_FOUND)
    {
        // 如果获取时发生除“未找到”之外的其他错误，则返回错误
        pthread_mutex_unlock(&manager->mutex); // 解锁并返回
        return ret;
    }
    // 如果 ret == SYSTEM_ERR_HASHMAP_NOT_FOUND, 则这是一个新fd, 无需移除, 直接继续添加流程

    // 将文件描述符添加到新的哈希集合中, 并获取新索引
    int new_index;
    ret = ring_enqueue(&manager->ring_queue, fd, &new_index);
    if (ret != SYSTEM_ERR_OK)
    {
        pthread_mutex_unlock(&manager->mutex); // 解锁并返回
        return ret;                            // 如果入队失败，则返回错误
    }

    // 更新或插入全局位置映射
    ret = hash_map_put(&manager->position_map, fd, new_index);
    if (ret != SYSTEM_ERR_OK && ret != SYSTEM_ERR_HASHMAP_ALREADY_EXISTS)
    {
        pthread_mutex_unlock(&manager->mutex); // 解锁并返回
        return ret;                            // 如果更新/插入失败，则返回错误
    }

    pthread_mutex_unlock(&manager->mutex); // 解锁
    return SYSTEM_ERR_OK;                  // 更新成功
}

system_error_code_t timeout_manager_tick(timeout_manager_t *manager)
{
    // 参数检查
    if (manager == NULL)
    {
        return SYSTEM_ERR_INVALID_ARGUMENT;
    }

    pthread_mutex_lock(&manager->mutex); // 加锁

    // 将环形队列的当前索引位置前进一位
    manager->ring_queue.current_index = (manager->ring_queue.current_index + 1) % manager->ring_queue.capacity;
    // 获取当前索引位置的哈希集合，即过期文件描述符集合
    hash_set_t *expired_set = &manager->ring_queue.set[manager->ring_queue.current_index];
    // 如果哈希集合为空，则没有过期文件描述符
    if (expired_set->map.count == 0)
    {
        pthread_mutex_unlock(&manager->mutex); // 解锁
        return SYSTEM_ERR_OK;                  // 无过期文件描述符，正常返回
    }

    int expired_count = expired_set->map.count;
    int *expired_fds = NULL;
    system_error_code_t ret = hash_set_get_all_fds(expired_set, &expired_fds);
    if (ret != SYSTEM_ERR_OK)
    {
        pthread_mutex_unlock(&manager->mutex); // 解锁
        return ret;
    }

    ret = hash_set_clear(expired_set);
    if (ret != SYSTEM_ERR_OK)
    {
        free(expired_fds);                     // 清理失败也要释放内存
        pthread_mutex_unlock(&manager->mutex); // 解锁
        return ret;
    }

    for (int i = 0; i < expired_count; i++)
    {
        hash_map_remove(&manager->position_map, expired_fds[i]);
    }

    // 所有内部数据结构的操作都已完成，可以解锁
    pthread_mutex_unlock(&manager->mutex);

    // 在锁外部执行回调，直接使用 expired_fds
    if (expired_fds != NULL)
    {
        for (int i = 0; i < expired_count; i++)
        {
            if (manager->timeout_callback != NULL)
            {
                manager->timeout_callback(expired_fds[i]);
            }
        }
        // 使用完毕后，释放它
        free(expired_fds);
    }

    return SYSTEM_ERR_OK; // 滴答成功
}